/**
 * 
 */
package file;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

/**
 * @author acidflow
 *
 */
public class SharedFiles{

	private static final byte _DELIMITER_NO = 0x00;
	private static final byte _DELIMITER_YES = 0x01;
	/**
	 * Récupère les fichier lisible du dossier path et rempli les objets File
	 * @param path Chemin vers le dossier contenant des fichiers partagés
	 * @param encryption Type de hashage
	 * @param piecesSize Taille des pièces
	 * @return la liste des objets File traités et remplis
	 * @throws FileNotFoundException
	 * @throws Exception
	 */
	public static ArrayList<File> getSharedFileFromFolder(String path, String encryption, int piecesSize, boolean seed) throws FileNotFoundException, Exception{
		ArrayList<File> files = new ArrayList<File>();
		java.io.File folder = new java.io.File(path	);
		java.io.File[] filesArray = folder.listFiles();
		for(int i = 0; i < filesArray.length; i++){
			if(filesArray[i].isFile() && filesArray[i].canRead()){
				File f = new File();
				f.setIdFile(new FileCheckSum(encryption, filesArray[i].getAbsolutePath()).getCheckSum());
				f.setName(filesArray[i].getName());
				f.setPath(filesArray[i].getAbsolutePath());
				f.setSize(filesArray[i].length());
				f.setPieceSize(piecesSize);
				setBufferMap(filesArray[i], f,piecesSize, seed);
				files.add(f);
			}
		}
		return files;
	}

	/**
	 * Rempli le BufferMap d'un fichier
	 * @param f le fichier sur le disque
	 * @param file le fichier du client
	 * @param piecesSize la taille d'une pièce
	 * @param seed boolean indiquant si le fichier est un seed
	 * @throws IOException
	 */
	private static void setBufferMap(java.io.File f, File file, int piecesSize, boolean seed) throws IOException{
		int nbPieces = (!seed) ? (int) Math.ceil((  (float) (f.length())/ (float) (piecesSize +1))) : (int) Math.ceil((  (float) (f.length())/ (float) (piecesSize)));
		byte[] checkPart = new byte[1];
		BufferMap bufferMap = new BufferMap(nbPieces);
		FileInputStream fis = new FileInputStream(f);
		BufferedInputStream bis = new BufferedInputStream(fis);
		DataInputStream dis = new DataInputStream(bis);
		if(!seed){

			for(int i = 0; i < nbPieces -1 ; i++){
				dis.read(checkPart);
				if(checkPart[0] == _DELIMITER_NO){
					bufferMap.removeOnePieceFromBufferMap(i);
				}
				else{

					bufferMap.addOnePieceToBufferMap(i);
				}
				dis.skipBytes(piecesSize);
			}
			dis.read(checkPart);
			if(checkPart[0] == _DELIMITER_NO){
				bufferMap.removeOnePieceFromBufferMap(nbPieces-1);
			}
			else{
				bufferMap.addOnePieceToBufferMap(nbPieces-1);
			}
			fis.close();
			bis.close();
			dis.close();
		}
		else{
			int nbPiecesSeed = (int) Math.ceil((  (float) (f.length())/ (float) piecesSize));
			for(int i = 0; i < nbPiecesSeed; i++){
				bufferMap.addOnePieceToBufferMap(i);
			}
		}
		file.setBufferMap(bufferMap);
	}

	/*private static boolean containsNull(byte[] b){
		for(int i = 0; i < b.length; i++){
			//TODO Cette constante ne marche pas pour délimiter une partie dans un fichier
			if(b[i] == 0x00){
				return true;
			}
		}
		return false;
	}*/

	/**
	 * Crée un fichier dans le dossier de leech pour pouvoir y écrire
	 * @param leechFolder le chemin du dossier de leech
	 * @param name le nom du fichier
	 * @param fileLenght la taille du fichier
	 * @param piecesSize la taille d'une pièce
	 */
	public static String createLeechFile(String leechFolder, String name, long fileLenght, int piecesSize){
		int nbPieces = (int) Math.ceil((  (float) fileLenght / (float) piecesSize));
		java.io.File folder = new java.io.File(leechFolder);
		try {
			java.io.File f = new java.io.File(leechFolder, name);
			RandomAccessFile bis = new RandomAccessFile(f, "rw");
			bis.setLength(fileLenght+nbPieces);
			byte[] delimiter = new byte[1];
			delimiter[0] = _DELIMITER_NO;
			for(int i = 0; i < nbPieces; i++){
				bis.write(delimiter[0]);
				bis.skipBytes(piecesSize);
			}
			bis.close();
			return f.getAbsolutePath();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Récupère la pièce de numéro pieceId dans le fichier file
	 * @param file le fichier
	 * @param pieceId le numéro de la pièce
	 * @param pieceSize	la taille d'une pièce
	 * @return	le tableau d'octect correspondant à la pièce
	 * @throws IOException
	 */
	public static byte[] getPiece(File file, int pieceId, long pieceSize) throws IOException{
		java.io.File f = new java.io.File(file.getPath());
		long goTo;
		byte[] pieceData = new byte[(int) pieceSize];
		RandomAccessFile bis = new RandomAccessFile(f, "r");
		if(file.getBufferMap().isComplete())
			goTo = pieceSize*pieceId;
		else
			goTo = (pieceSize + 1)*pieceId;
		bis.seek(goTo);
		bis.read(pieceData);
		bis.close();
		return pieceData;
	}

	/**
	 * Sauvegarde la pièce d'indice pieceId sur le disque
	 * @param file le fichier
	 * @param pieceId le numéro de la pièce
	 * @param data les données de la pièce
	 * @throws IOException
	 */
	public static void setPiece(File file, int pieceId, byte[] data) throws IOException{
		
		java.io.File f = new java.io.File(file.getPath());
		long goTo;
		byte[] delimiterOk = new byte[1];
		delimiterOk[0] = _DELIMITER_YES;
		RandomAccessFile bis = new RandomAccessFile(f, "rw");
		goTo = (file.getPieceSize() + 1)*pieceId;
		bis.seek(goTo);
		bis.write(delimiterOk);
		bis.write(data);
		bis.close();
	}
	
	/**
	 * Passe le fichier dont le téléchargement est terminé dans les seed et le supprime des leech
	 * @param f le fichier
	 */
	public static void moveToSeed(File f){
		String pathToNewSeed = "seed/"+f.getName();
		java.io.File newSeed = new java.io.File(pathToNewSeed);
		java.io.File leechComplete = new java.io.File(f.getPath());
		byte[] data = new byte[(int) f.getPieceSize()];
		try {
			RandomAccessFile tmpRead = new RandomAccessFile(leechComplete, "r");
			RandomAccessFile tmpWrite = new RandomAccessFile(newSeed, "rw");
			int nbPieces = f.getBufferMap().getLength();
			for(int i = 0; i < nbPieces; i++){
				tmpRead.read();
				tmpRead.read(data);
				tmpWrite.write(data);
			}
			tmpRead.close();
			tmpWrite.close();
			leechComplete.delete();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
